//
// Created by guoshunw on 1/30/2021.
//

#include "AudioMixer.h"

#include <cstdio>

short remix(short b1, short b2) {
    const double REMINDER = 32767.0;
//#define SIMPLE
#ifdef SIMPLE
    return static_cast<short>(b1 + b2 / 2);
#else
    return static_cast<short>(b1 + b2 - ((b1 < 0 && b2 < 0 ? -1 : 1) * b1 * b2 / REMINDER));
#endif
}

#define SIZE_AUDIO_FRAME (2)

void Mix(char sourseFile[10][SIZE_AUDIO_FRAME], int number, char *objectFile) {
    //归一化混音
    int const MAX = 32767;
    int const MIN = -32768;

    double f = 1;
    int output;
    int i = 0, j = 0;
    for (i = 0; i < SIZE_AUDIO_FRAME / 2; i++) {
        int temp = 0;
        for (j = 0; j < number; j++) {
            temp += *(short *) (sourseFile[j] + i * 2);
        }
        output = (int) (temp * f);
        if (output > MAX) {
            f = (double) MAX / (double) (output);
            output = MAX;
        } else if (output < MIN) {
            f = (double) MIN / (double) (output);
            output = MIN;
        }
        if (f < 1) {
            f += ((double) 1 - f) / (double) 32;
        }
        *(short *) (objectFile + i * 2) = (short) output;
    }
}

int mixer(const char *f1, const char *f2, const char *outFile) {
    FILE *fp1 = fopen(f1, "rb");
    FILE *fp2 = fopen(f2, "rb");

    if (nullptr == fp1 || nullptr == fp2) {
        printf("%s or %s doesn't exist.\n", f1, f2);
        return -1;
    }

    FILE *out = fopen(outFile, "wb");
    if (nullptr == out) {
        printf("Create file %s failed.\n", outFile);
        return -1;
    }

    printf("Begin mixing %s and %s into %s\n", f1, f2, outFile);

    short d1, d2, dmix;

    // at most 10 source, each source audio frame size is SIZE_AUDIO_FRAME


    while (true) {
        int r1 = fread(&d1, 2, 1, fp1);
        int r2 = fread(&d2, 2, 1, fp2);
        bool valid = true;
        if (0 == r1 && 0 == r2) break;

        if (r1 > 0 && r2 > 0) { // both r1 and r2 not >0
            dmix = remix(d1, d2);

//            int sourceIndex = 0;
//            char source[10][SIZE_AUDIO_FRAME];
//            *reinterpret_cast<short *>(source[sourceIndex++]) = d1;
//            *reinterpret_cast<short *>(source[sourceIndex++]) = d2;
//            Mix(source, sourceIndex, reinterpret_cast<char *>(&dmix));
//
//            if (dmix > (pow(2, 15) - 1) || dmix < -pow(2, 15)) {
//                printf("mix error\n");
//            }

        } else if (0 == r1 && r2 > 0) {
            dmix = d2;
        } else if (0 == r2 && r1 > 0) {
            dmix = d1;
        } else {
            printf("Invalid data, ignore...\n");
            valid = false;
        }

        if (valid) {
            int ret = fwrite(&dmix, 2, 1, out);
            if (ret < 1) {
                printf("Write %d to out file %s failed: %d\n", dmix, outFile, ret);
                return ret;
            }
        }
    }

    fclose(fp1);
    fclose(fp2);
    fclose(out);
    return 0;
}

void testMixer() {
    /*
     * File format: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 22050 Hz, 1 channels, s16, 352 kb/s
     * ffmpeg -i ring03mono.wav -fs16le -acodec pcm_s16le -ac 1 -ar 44100 r1.pcm
     * ffmpeg -i ring09mono.wav -fs16le -acodec pcm_s16le -ac 1 -ar 44100 r2.pcm
     *
     * ffplay -f s16le -ac 1 -ar 44100 rout.pcm
     * */
    const char *f1 = "../resources/r1.pcm";
    const char *f2 = "../resources/r2.pcm";
    const char *out = "../resources/rout1.pcm";
    int ret = mixer(f1, f2, out);
    if (ret < 0) {
        printf("Mixing %s and %s failed.\n", f1, f2);
    } else {
        printf("Mixing %s and %s successfully.\n", f1, f2);
    }
}
